﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.IO;
using System.Globalization;

namespace Engine.Sgml {
    class XmlTextReader : XmlReader {
        private SElement element;
        private SObject node;
        private SAttribute attribute;
        private SgmlNodeType nodeType;

        internal XmlTextReader(SElement document) {
            Guard.ArgumentNotNull(document, "document");
            element = document;
            node = element;
            nodeType = document.NodeType;
            }

        public override int AttributeCount {
            get { return element.Attributes.Count; }
            }

        public override string BaseURI {
            get { return null; }
            }

        public override void Close() {
            return;
            }

        public override int Depth {
            get { return element.Depth; }
            }

        public override bool EOF {
            get { return ReadState == System.Xml.ReadState.EndOfFile; }
            }

        public override string GetAttribute(int i) {
            SAttribute attributre = element.Attributes[i];
            if (object.ReferenceEquals(attributre, null))
                throw new ArgumentException("Could not found attribute with specified index.");
            else
                return attributre.Value;
            }

        public override string GetAttribute(string name, string namespaceURI) {
            return GetAttribute(name);
            }

        public override string GetAttribute(string name) {
            SAttribute attribute = element.Attributes[name];
            if (object.ReferenceEquals(attribute, null))
                return null;
            else
                return attribute.Value;
            }

        public override bool IsEmptyElement {
            get { return element.IsEmpty || element.Nodes.Count == 0; }
            }

        public override string LocalName {
            get { return node.LocalName; }
            }

        public override string LookupNamespace(string prefix) {
            throw new NotSupportedException();
            }

        public override bool MoveToAttribute(string name, string ns) {
            return MoveToAttribute(name);
            }

        public override bool MoveToAttribute(string name) {
            attribute = element.Attributes[name];
            return attribute != null;
            }

        public override bool MoveToElement() {
            if (node.Parent == null)
                return false;
            element = node.Parent;
            node = element;
            nodeType = element.NodeType;
            return true;
            }

        public override bool MoveToFirstAttribute() {
            if (element.HasAttributes) {
                attribute = element.Attributes.FirstElement;
                if (attribute.IsNamespace() && SAttribute.NamespaceReserved(attribute.Value))
                    return MoveToNextAttribute();
                else {
                    node = attribute;
                    nodeType = SgmlNodeType.Attribute;
                    attributeValueReaded = false;
                    return true;
                    }
                }
            else
                return false;
            }

        public override bool MoveToNextAttribute() {
            if (object.ReferenceEquals(attribute, null))
                attribute = element.FirstAttribute;
            else
                attribute = attribute.NextAttribute;
            if (object.ReferenceEquals(attribute, null))
                return false;
            if (attribute.IsNamespace() && SAttribute.NamespaceReserved(attribute.Value))
                return MoveToNextAttribute();
            else {
                node = attribute;
                nodeType = SgmlNodeType.Attribute;
                attributeValueReaded = false;
                return true;
                }
            }

        public override XmlNameTable NameTable {
            get { return SName.NameTable; }
            }

        public override string NamespaceURI {
            get { return string.Empty; }
            }

        public override XmlNodeType NodeType {
            get {
                switch (nodeType) {
                    case SgmlNodeType.None:
                        return XmlNodeType.None;
                    case SgmlNodeType.DocumentType:
                        return XmlNodeType.DocumentType;
                    case SgmlNodeType.Document:
                        return XmlNodeType.Document;
                    case SgmlNodeType.Element:
                        return XmlNodeType.Element;
                    case SgmlNodeType.Attribute:
                        if (attributeValueReaded)
                            return XmlNodeType.Text;
                        else
                            return XmlNodeType.Attribute;
                    case SgmlNodeType.WhiteSpace:
                        return XmlNodeType.Whitespace;
                    case SgmlNodeType.SignificantWhiteSpace:
                        return XmlNodeType.SignificantWhitespace;
                    case SgmlNodeType.CData:
                    case SgmlNodeType.Text:
                        return XmlNodeType.Text;
                    case SgmlNodeType.Comment:
                        return XmlNodeType.Comment;
                    case SgmlNodeType.EndElement:
                        return XmlNodeType.EndElement;
                    case SgmlNodeType.CDataSection:
                        return XmlNodeType.CDATA;
                    case SgmlNodeType.Condition:
                        return XmlNodeType.Text;
                    case SgmlNodeType.ProcessingInstruction:
                        if (element.IsXmlDeclaration())
                            return XmlNodeType.XmlDeclaration;
                        else
                            return XmlNodeType.ProcessingInstruction;
                    default:
                        throw new InvalidOperationException("Unknown the node type of an element.");
                    }
                }
            }

        public override string Prefix {
            get { return node.Prefix; }
            }

        public override bool Read() {
            switch (ReadState) {
                case ReadState.Closed:
                    return false;
                case ReadState.EndOfFile:
                    return false;
                case ReadState.Error:
                    return false;
                case ReadState.Initial:
                    if (element.HasElements && MoveToFirstChildElement()) {
                        readState = System.Xml.ReadState.Interactive;
                        return true;
                        }
                    else {
                        readState = System.Xml.ReadState.EndOfFile;
                        return false;
                        }
                case ReadState.Interactive:
                    if (nodeType == SgmlNodeType.EndElement)
                        return MoveToNextElement();
                    if (element.HasElements)
                        return (MoveToFirstChildElement() || MoveToNextElement());
                    else
                        return MoveToNextElement();
                default:
                    throw new InvalidOperationException("Unknown the read state.");
                }
            }

        private bool MoveToFirstChildElement() {
            if (element.HasElements) {
                element = element.Nodes.FirstElement;
                nodeType = element.NodeType;
                node = element;
                return true;
                }
            else
                return false;
            }

        private bool MoveToNextElement() {
            SElement nextNode = element.NextNode;
            if (object.ReferenceEquals(nextNode, null)) {
                if (object.ReferenceEquals(element.Parent, null) || element.Parent.NodeType == SgmlNodeType.Document) {
                    readState = System.Xml.ReadState.EndOfFile;
                    return false;
                    }
                element = element.Parent;
                nodeType = SgmlNodeType.EndElement;
                }
            else {
                element = nextNode;
                nodeType = element.NodeType;
                }
            node = element;
            return true;
            }

        private bool attributeValueReaded;
        public override bool ReadAttributeValue() {
            if (attributeValueReaded)
                return false;
            attributeValueReaded = true;
            return true;
            }

        private ReadState readState;
        public override ReadState ReadState {
            get { return readState; }
            }

        public override void ResolveEntity() {
            throw new NotSupportedException();
            }

        public override string Value {
            get {
                return node.Value;
                }
            }

        public override bool HasValue {
            get { return !string.IsNullOrEmpty(node.Value); }
            }
        }
    }
